﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Threading;
using System.Xml;

namespace Common
{
    /// <summary>
    /// 配置文件基类，可自定义配置文件路径
    /// </summary>
    public class LocalBase
    {
        /// <summary>
        /// 配置文件的路径
        /// </summary>
        private string _ConfigFilePath;
        public string ConfigFilePath
        {
            get
            {
                //不存在时创建
                if (!File.Exists(_ConfigFilePath) || new FileInfo(_ConfigFilePath).Length == 0)
                {
                    ResetConfigFile();
                }

                return _ConfigFilePath;
            }
            set
            {
                _ConfigFilePath = value;
            }
        }
        private static object lockObj = new object();
        private XmlDocument _ConfigDoc = null;

        public LocalBase(string configFilePath)
        {
            _ConfigFilePath = configFilePath;
            //不存在时创建
            if (!File.Exists(_ConfigFilePath) || new FileInfo(_ConfigFilePath).Length == 0)
            {
                ResetConfigFile();
            }
            ResetXmlDoc();
        }
        /// <summary>
        /// 重置LocalConfig.
        /// </summary>
        private void ResetConfigFile()
        {
            try
            {
                //配置的备份功能暂时未做
                //string bakConfigFile = Path.Combine(Path.GetDirectoryName(_ConfigFilePath), "Bak/" + Path.GetFileName(_ConfigFilePath));//20110410 Code By Bin 从备份文件夹中获取配置文件
                //if (File.Exists(bakConfigFile))
                //{
                //    _ConfigDoc = new XmlDocument();
                //    File.Copy(bakConfigFile, _ConfigFilePath, true);
                //    StreamReader sReader = new StreamReader(_ConfigFilePath, System.Text.Encoding.GetEncoding("UTF-8"));
                //    try
                //    {
                //        string tStr = sReader.ReadToEnd();
                //        _ConfigDoc.LoadXml(tStr);
                //        return;
                //    }
                //    catch
                //    {

                //    }
                //    finally
                //    {
                //        sReader.Close();
                //    }
                //}
                _ConfigDoc = new XmlDocument();
                XmlProcessingInstruction myProc = _ConfigDoc.CreateProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");//encoding="GB2312"?>   UTF-8
                _ConfigDoc.AppendChild(myProc);
                XmlElement root = _ConfigDoc.CreateElement("Root");
                _ConfigDoc.AppendChild(root);
                Directory.CreateDirectory(Path.GetDirectoryName(_ConfigFilePath));
                _ConfigDoc.Save(_ConfigFilePath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 重置ConfigXml数据对象 Code By Bin、Qiang 20110216 为了修复解决LocalConfig有时被置空、编码发生错误导致无法读取
        /// 20110302 在配置文件不存在时StreamReader初始化便报错导致始终无法创建配置文件，如PageListView的配置调用
        /// 20110328 曾文斌 将方法开放，在需要程序间同步配置时，可以先调用该方法再读取配置文件
        /// </summary>
        public void ResetXmlDoc()
        {
            lock (lockObj)
            {
                XmlDocument temp = new XmlDocument();
                try
                {
                    StreamReader sReader = new StreamReader(_ConfigFilePath, System.Text.Encoding.GetEncoding("UTF-8"));
                    try
                    {
                        string tStr = sReader.ReadToEnd();
                        temp.LoadXml(tStr);
                        _ConfigDoc = temp;
                    }
                    catch (Exception ex)
                    {
                        sReader.Close();//首先关闭读取流，否则重置失败 20110424 code by bin
                        //重置前，先备份
                        #region 备份

                        #endregion
                        //ResetConfigFile();
                    }
                    finally
                    {
                        sReader.Close();
                    }
                }
                catch
                {
                    //重置前，先备份
                    #region 备份

                    #endregion
                    //ResetConfigFile();
                }
            }
        }
        /// <summary>
        /// 由项目，分类，键名 获取键值
        /// </summary>
        /// <param name="category">分类</param>
        /// <param name="key">键</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns></returns>
        public string GetValue(string category, string key, string defaultValue)
        {
            try
            {
                string strResult = defaultValue;
                bool isExists = false;
                lock (lockObj)
                {
                    string xpath = "/Root/Category[@key=\"" + category + "\"]/Item[@key=\"" + key + "\"]";
                    XmlNode n = _ConfigDoc.SelectSingleNode(xpath);
                    if (n != null)
                    {
                        strResult = n.InnerText;
                        isExists = true;
                    }
                }
                if (!isExists)
                {
                    SetValue(category, key, defaultValue, true);
                }
                return strResult;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 分类，键名,设置键值
        /// </summary>
        /// <param name="category"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetValue(string category, string key, string value, bool isGetInvoke)
        {
            SetValue(category, key, value, string.Empty, string.Empty, false);
        }

        public void SetValue(string category, string key, string value)
        {
            SetValue(category, key, value, false);
        }

        public void SetValue(string category, string key, int value)
        {
            SetValue(category, key, value.ToString());
        }
        public void SetValue(string category, string key, float value)
        {
            SetValue(category, key, value.ToString());
        }
        public void SetValue(string category, string key, double value)
        {
            SetValue(category, key, value.ToString());
        }
        public void SetValue(string category, string key, short value)
        {
            SetValue(category, key, value.ToString());
        }
        public void SetValue(string category, string key, byte value)
        {
            SetValue(category, key, value.ToString());
        }

        public void SetValue(string category, string key, string value, string sMemo, string sType)
        {
            SetValue(category, key, value, sMemo, sType, false);
        }

        public void SetValue(string category, string key, string value, string sMemo, string sType, bool isGetInvoke)
        {
            try
            {
                lock (lockObj)
                {
                    string xpath = "/Root/Category[@key=\"" + category + "\"]";
                    XmlNode n = _ConfigDoc.SelectSingleNode(xpath);
                    if (n == null)
                    {
                        //create category
                        XmlElement categoryNode = _ConfigDoc.CreateElement("Category");
                        XmlAttribute categoryAttri = _ConfigDoc.CreateAttribute("key");
                        categoryAttri.Value = category;
                        categoryNode.Attributes.Append(categoryAttri);

                        XmlElement itemNode = _ConfigDoc.CreateElement("Item");
                        XmlAttribute itemAttri = _ConfigDoc.CreateAttribute("key");
                        itemAttri.Value = key;
                        itemNode.Attributes.Append(itemAttri);

                        XmlAttribute itemAttriMemo = _ConfigDoc.CreateAttribute("Memo");
                        itemAttriMemo.Value = sMemo;
                        itemNode.Attributes.Append(itemAttriMemo);

                        XmlAttribute itemAttriType = _ConfigDoc.CreateAttribute("Type");
                        itemAttriType.Value = sType;
                        itemNode.Attributes.Append(itemAttriType);

                        itemNode.InnerText = value;

                        categoryNode.AppendChild(itemNode);

                        n = _ConfigDoc.SelectSingleNode("/Root");
                        n.AppendChild(categoryNode);
                    }
                    else
                    {
                        xpath = "/Root/Category[@key=\"" + category + "\"]/Item[@key=\"" + key + "\"]";
                        XmlNode n2 = _ConfigDoc.SelectSingleNode(xpath);
                        if (n2 != null)
                        {
                            if (!isGetInvoke)
                            {
                                string strOldValue = n2.InnerText;
                                if (string.Equals(strOldValue, value))//XMDXFSZSYY-44 如果写入的值与读取的值一致，不再写入
                                {
                                    return;
                                }
                            }
                            n2.RemoveAll();
                            XmlAttribute itemAttri = _ConfigDoc.CreateAttribute("key");
                            itemAttri.Value = key;
                            n2.Attributes.Append(itemAttri);

                            XmlAttribute itemAttriMemo = _ConfigDoc.CreateAttribute("Memo");
                            itemAttriMemo.Value = sMemo;
                            n2.Attributes.Append(itemAttriMemo);

                            XmlAttribute itemAttriType = _ConfigDoc.CreateAttribute("Type");
                            itemAttriType.Value = sType;
                            n2.Attributes.Append(itemAttriType);
                            n2.InnerText = value;
                        }
                        else
                        {
                            //create item
                            XmlElement itemNode = _ConfigDoc.CreateElement("Item");
                            XmlAttribute itemAttri = _ConfigDoc.CreateAttribute("key");
                            itemAttri.Value = key;
                            itemNode.Attributes.Append(itemAttri);

                            XmlAttribute itemAttriMemo = _ConfigDoc.CreateAttribute("Memo");
                            itemAttriMemo.Value = sMemo;
                            itemNode.Attributes.Append(itemAttriMemo);

                            XmlAttribute itemAttriType = _ConfigDoc.CreateAttribute("Type");
                            itemAttriType.Value = sType;
                            itemNode.Attributes.Append(itemAttriType);
                            itemNode.InnerText = value;
                            n.AppendChild(itemNode);
                        }
                    }
                    FileUtil.WaitForFileNotUsing(_ConfigFilePath, 1000);//XMDXFSZSYY-44 规避多个线程读写同一个配置文件导致报错问题
                    _ConfigDoc.Save(_ConfigFilePath);
                    ResetXmlDoc();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 删除某个主节点的某个子节点
        /// </summary>
        /// <param name="category">分类</param>
        /// <param name="key">键</param>
        public void DelValue(string category, string key)
        {
            try
            {
                lock (lockObj)
                {
                    string xpath = "/Root/Category[@key=\"" + category + "\"]/Item[@key=\"" + key + "\"]";
                    XmlNode n = _ConfigDoc.SelectSingleNode(xpath);
                    if (n != null)
                    {
                        n.ParentNode.RemoveChild(n);
                        _ConfigDoc.Save(_ConfigFilePath);
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获取某个主节点的所有子节点.
        /// </summary>
        /// <param name="category">The category.</param>
        /// <returns></returns>
        public Dictionary<string, string> GetKeyValueList(string category)
        {
            try
            {
                lock (lockObj)
                {
                    Dictionary<string, string> dictionary = new Dictionary<string, string>();
                    DataSet ds = new DataSet();
                    ds.ReadXml(_ConfigFilePath);
                    //判断是否存在key为category的节点，
                    //否则ds.Tables["Category"].Select("key='" + category + "'")[0]索引越界
                    if (ds.Tables["Category"].Select("key='" + category + "'").Length > 0)
                    {
                        DataRow[] drsList = ds.Tables["item"].Select("Category_Id='" + ds.Tables["Category"].Select("key='" + category + "'")[0]["Category_Id"].ToString() + "'");
                        for (int iCount = 0; iCount < drsList.Length; iCount++)
                        {
                            dictionary.Add(drsList[iCount][0].ToString(), drsList[iCount][3].ToString());
                        }
                        return dictionary;
                    }
                    else
                    {
                        return new Dictionary<string, string>();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获取所有分类节点
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, string> GetCategoryList()
        {
            try
            {
                lock (lockObj)
                {
                    Dictionary<string, string> dictionary = new Dictionary<string, string>();
                    DataSet ds = new DataSet();
                    ds.ReadXml(_ConfigFilePath);
                    DataRow[] drsList = ds.Tables["Category"].Select();
                    for (int iCount = 0; iCount < drsList.Length; iCount++)
                    {
                        dictionary.Add(drsList[iCount][1].ToString(), drsList[iCount][1].ToString());
                    }
                    return dictionary;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

    /// <summary>
    /// 文件助手类
    /// </summary>
    public class FileUtil
    {
        /// <summary>
        /// 判断文件是否占用.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>
        /// 	<c>true</c> if [is file in use] [the specified file name]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsFileInUse(string fileName)
        {
            try
            {
                using (File.Open(fileName, FileMode.Open)) { }
            }
            catch (IOException e)
            {
                var errorCode = System.Runtime.InteropServices.Marshal.GetHRForException(e) & ((1 << 16) - 1);

                return errorCode == 32 || errorCode == 33;
            }

            return false;
        }
        public static void WaitForFileNotUsing(string filePath, int millisecondsTimeout)
        {
            bool isLocked;
            WaitForFileNotUsing(filePath, millisecondsTimeout, out isLocked);
        }
        /// <summary>
        /// 等待文件变成未占用状态.
        /// </summary>
        /// <param name="filePath">文件路径.</param>
        /// <param name="millisecondsTimeout">超时时间，小于等于0表示永久等待.</param>
        public static void WaitForFileNotUsing(string filePath, int millisecondsTimeout, out bool isLocked)
        {
            if (File.Exists(filePath))
            {
                isLocked = true;
                int i = 0;
                while (isLocked
                    && (i < millisecondsTimeout
                    || millisecondsTimeout <= 0))
                {
                    if (IsFileInUse(filePath))
                    {
                        i += 250;
                        Thread.Sleep(250);
                    }
                    else
                    {
                        isLocked = false;
                    }
                }
            }
            else
            {
                isLocked = false;
            }
        }
    }
}
